package com.example.gkb_signalr

import android.os.Handler
import android.os.Looper
import com.microsoft.signalr.HubConnection
import com.microsoft.signalr.HubConnectionBuilder
import com.microsoft.signalr.HubConnectionState
import io.flutter.plugin.common.MethodChannel
import io.reactivex.rxjava3.core.CompletableObserver
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable

enum class CallMethod(val value: String) {
    ConnectToServer("connectToServer"),
    Reconnect("reconnect"),
    Stop("stop"),
    IsConnected("isConnected"),
    AckMessage("ackMessage"),
}

object SignalR {

    private lateinit var hubConnection: HubConnection
    private val mHandler = Handler(Looper.getMainLooper())
    fun connectToServer(
        url: String,
        token: String,
        result: MethodChannel.Result,
        channel: MethodChannel,
    ) {
        try {
            hubConnection = HubConnectionBuilder
                .create(url)
                .withAccessTokenProvider(Single.defer {
                    Single.just(token)
                }).build()
            hubConnection.onClosed {
                statusChang(channel)
            }
            onMessage(result, channel)
            startConnect(channel)
        } catch (ex: Exception) {
            result.error(ex.localizedMessage, ex.stackTrace.toString(), null)
        }
    }


    private fun onMessage(result: MethodChannel.Result, channel: MethodChannel) {
        try {
            hubConnection.on("ReceiveMessage",
                {
                    println("ReceiveMessage Message: $it")
                    mHandler.post {
                        channel.invokeMethod("ReceiveMessage", it)
                    }
                }, String::class.java)

        } catch (ex: Exception) {
            result.error(ex.localizedMessage, ex.stackTrace.toString(), null)
        }

    }

    fun ackMessage(ids: List<Int>) {
        hubConnection.invoke("AckMessage", ids)
    }

    fun stop(result: MethodChannel.Result) {
        try {
            hubConnection.stop()
        } catch (ex: Exception) {
            result.error(ex.localizedMessage, ex.stackTrace.toString(), null)
        }
    }

    fun isConnected(result: MethodChannel.Result) {
        try {
            if (this::hubConnection.isInitialized) {
                when (hubConnection.connectionState) {
                    HubConnectionState.CONNECTED -> result.success(true)
                    else -> result.success(false)
                }
            } else {
                result.success(false)
            }
        } catch (ex: Exception) {
            result.error("Error", ex.localizedMessage, null)
        }
    }

    fun reconnect(result: MethodChannel.Result, channel: MethodChannel) {
        try {
            startConnect(channel)
        } catch (ex: Exception) {
            result.error(ex.localizedMessage, ex.stackTrace.toString(), null)
        }
    }


    private fun startConnect(channel: MethodChannel) {
        hubConnection.start().subscribe(object : CompletableObserver {
            override fun onSubscribe(d: Disposable) {
            }

            override fun onComplete() {
                statusChang(channel)
            }

            override fun onError(e: Throwable) {
                statusChang(channel)
            }

        })
    }

    private fun statusChang(channel: MethodChannel) {
        mHandler.post {
            channel.invokeMethod("ConnectionStatus",
                hubConnection.connectionState == HubConnectionState.CONNECTED)
            if (hubConnection.connectionState == HubConnectionState.CONNECTED) {
                reconnectCount = 0
            }
            if (hubConnection.connectionState == HubConnectionState.DISCONNECTED && reconnectCount < 3) {
                mHandler.postDelayed({
                    reconnectCount++
                    startConnect(channel)
                }, 3000)
            }else{
                reconnectCount = 0
            }
        }
    }

    private var reconnectCount = 0
}
